home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / oper_sys / presto / prest1_0.lha / src / preempt.C < prev    next >
C/C++ Source or Header  |  1991-12-11  |  18KB  |  767 lines

  1. /*
  2.  * preempt.c
  3.  *
  4.  *
  5.  * Preemption interrupt routines.
  6.  *
  7.  * Preemption works by having a single designated process(or) take a VTALRM
  8.  * every Scheduler::sc_quantum milliseconds.  The designated process
  9.  * takes a snapshot of the readyq, estimating how many ready threads
  10.  * there are.  For each processor, if that processor is running a 
  11.  * preemptable thread (scheduler threads are non-preemptable), and
  12.  * the thread has been running for longer than the quantum, the processor
  13.  * on which the thread is running is handed a a SIGUSR1, to force
  14.  * a resched.
  15.  *
  16.  * In the resched interrupt handler (or from the VTALRM handler if
  17.  * the designated alrm handler is running on a processor which needs
  18.  * to be preempted), we diddle with the return sigcontext so that on
  19.  * return from the signal handler, we will be running a preface to the
  20.  * actual thread switch routine, as though it were called directly
  21.  * by the user thread.  This makes context switching always appear 
  22.  * synchronous from the standpoint of the scheduler.
  23.  *
  24.  * The signal handling code here is (unfortunately) machine dependent and
  25.  * will need to be rewritten somewhat to run under 4.3 or the vax.
  26.  *
  27.  * Note that the MIPS port already uses machine independent methods
  28.  * of restoring context during preemption. This method only relies on
  29.  * the 4.3BSD sigcontext entry points (including the undocumented sigreturn()).
  30.  *
  31.  * This code needs to be made part of a general sigobject that interfaces
  32.  * PRESTO code to asynchronous events.
  33.  */
  34.  
  35.  
  36. #define _PREEMPT_C
  37.  
  38. #include <stddef.h>
  39. #include <sys/types.h>
  40. #include <signal.h>
  41. #include <osfcn.h>
  42. #include "presto.h"
  43.  
  44. //
  45. // On the sequent, each process has a private interrupts_enabled
  46. // variable.  On other machines, interrupts_enabled is implemented
  47. // as a field in each process object.  If interrupts are disabled,
  48. // then thisthread is, by definition, not preemptable.  A processor
  49. // could be running with interrupts enabled, but thisthread could
  50. // not be allowing preemption.  See functions for enabling and
  51. // disabling interrupts at the bottom of this file.
  52. //
  53.  
  54. #ifdef sequent
  55. private_t int interrupts_enabled = 1;
  56. #endif /* sequent */
  57. #ifdef sun
  58. #    ifndef THREAD_HAS_INTERRUPTIBLE_FIELD
  59.     private_t int interrupts_enabled = 1;
  60. #    endif
  61. #endif 
  62.  
  63. int disable_interrupts();
  64. void enable_interrupts();
  65. int interrupts_are_enabled();
  66.  
  67. int preemption_enabled = 0;    // preemption ticker is running
  68.  
  69. #ifdef PREEMPT
  70.  
  71. static private_t int    osigmask;
  72.  
  73. void sigpreempt_init    ();
  74. sigvec_handler_t sigpreempt_notify  (int sig, int code, struct sigcontext *scp);
  75. sigvec_handler_t sigpreempt_alrm    (int sig, int code, struct sigcontext *scp);
  76. void sigpreempt_unblock ();
  77.  
  78. //
  79. // signal reentry point
  80. //
  81.  
  82. shared_t int        numalarms  = 0;        // damn the concurrency
  83. shared_t int        numnotifies = 0;
  84. shared_t int         numfalsehits = 0;
  85.  
  86. #define SIGSTACKSIZ    4096
  87. #define SIGPREEMPT_NOTIFY    SIGUSR1
  88. #define SIGPREEMPT_ALRM        SIGVTALRM
  89.     
  90. //
  91. // Take the two preempt signals on the signal stack
  92. //
  93. static struct sigvec    svec_notify = { 
  94.     sigpreempt_notify, sigmask(SIGPREEMPT_NOTIFY), 1};
  95.  
  96. static struct sigvec    svec_alrm ={ 
  97.     sigpreempt_alrm, sigmask(SIGPREEMPT_ALRM), 1};
  98.  
  99. static private_t    int    sstack[SIGSTACKSIZ];
  100. static struct sigstack    signalstack = { (caddr_t)&sstack[SIGSTACKSIZ-1], 0 };
  101.  
  102.  
  103. // force kernel sigvec  to be called
  104. #ifdef sequent
  105. // #define SIGVEC_OS    _sigvec
  106. #define SIGVEC_OS       sigvec
  107. #else
  108. #define SIGVEC_OS    sigvec
  109. #endif
  110.  
  111. extern int SIGVEC_OS(int, const struct sigvec*, struct sigvec*);
  112.  
  113. void
  114. sigpreempt_init()
  115. {
  116.  
  117.     struct sigstack    oss;
  118.  
  119.     // Check if we are already running on an alternate stack
  120.     if (sigstack((struct sigstack*)0, &oss) < 0)    {
  121.         perror("sigstack1");
  122.         fatalerror();
  123.     }
  124.     
  125.     if (oss.ss_sp == 0)    {        // must set our own
  126.         if (sigstack(&signalstack, (struct sigstack*)0) < 0)    {
  127.             perror("sigstack2");
  128.             fatalerror();
  129.         }
  130.     }
  131.     
  132.     if (SIGVEC_OS(SIGPREEMPT_ALRM, &svec_alrm, 0) < 0)    {
  133.         perror("sigvecusr1");
  134.         fatalerror();
  135.     }
  136.     
  137.     if (SIGVEC_OS(SIGPREEMPT_NOTIFY, &svec_notify, 0) < 0)    {
  138.         perror("sigvecvtalrm");
  139.         fatalerror();
  140.     }
  141.  
  142.     osigmask = sigblock(0);        // get original signal mask
  143.     return;
  144. }
  145.  
  146.  
  147. void
  148. sigpreempt_stopclock()
  149. {
  150.     struct itimerval it;
  151.  
  152.     disable_interrupts();
  153.     preemption_enabled = 0;
  154.  
  155.     sigblock(sigmask(SIGPREEMPT_NOTIFY)|sigmask(SIGSEGV));
  156.     it.it_interval.tv_sec = 0;
  157.     it.it_interval.tv_usec = 0;
  158.     it.it_value.tv_sec = 0;
  159.     it.it_value.tv_usec = 0;
  160.     setitimer(ITIMER_VIRTUAL, &it, (struct itimerval*)0);
  161. }
  162.  
  163.  
  164. //
  165. // this should only run in the parent master scheduler
  166. //
  167. void
  168. sigpreempt_beginclock(struct timeval *quantum)
  169. {
  170.     struct itimerval it;
  171.  
  172.     preemption_enabled = 1;
  173.     if (quantum->tv_sec == 0 && quantum->tv_usec < 100000)
  174.         quantum->tv_usec = 100000;    // 100ms minimum quantum
  175.     it.it_interval = *quantum;
  176.     it.it_value = *quantum;
  177.     if (setitimer(ITIMER_VIRTUAL, &it, (struct itimerval*)0) < 0)    {
  178.         perror("setitimer");
  179.         fatalerror();
  180.     }
  181.     sigpreempt_unblock();
  182.     enable_interrupts();
  183. }
  184.  
  185.  
  186.  
  187. void
  188. sigpreempt_unblock()
  189. {
  190.     sigsetmask(osigmask);
  191. }
  192.  
  193.  
  194.  
  195.  
  196. //
  197. // handler for taking care of preemption when signaled.  We can be called
  198. //    synchronously:    if the vtalrm handler process needs to be preempted
  199. //            when done handling the alarm
  200. //
  201. //    asynchronously:
  202. //            if the process on which we are running needs to get
  203. //            preempted as decided by the vtalrm handler.
  204. //
  205. //    We block the offending signal for after we return until after
  206. //    the main preemption code has done its thing.
  207.  
  208.  
  209.  
  210. sigvec_handler_t
  211. sigpreempt_notify( int sig, int code, struct sigcontext *scp)
  212. {
  213. #ifdef mips
  214.         extern char atomic_test_and_set[];
  215.         extern char atomic_test_and_set_rcs[];
  216.     struct sigcontext *newscp;
  217.     void mips_preempt_reentry(struct sigcontext *, int);
  218.  
  219.     sig = sig;   // satisfy cfront
  220.     code = code; // satisfy cfront
  221. #else
  222.         int *sp = (int*)scp->sc_sp;
  223. #endif /* mips */
  224.  
  225.     /* check that we can preempt
  226.      */
  227.  
  228.     if (!interrupts_are_enabled()) {
  229.         code=code;        // make compiler happy
  230. #ifdef mips
  231.         return;
  232. #else
  233.         return 0;
  234. #endif
  235.     }
  236.  
  237. #ifdef    ns32000
  238.     sp[0] = scp->sc_modpsr;
  239. #endif
  240. #ifdef    i386
  241.     sp[0] = scp->sc_flags;
  242. #endif
  243. #ifdef sequent
  244.         sp[1] = scp->sc_pc;
  245.         scp->sc_sp -= 2 * sizeof(int);
  246. #endif /* sequent */
  247.  
  248. #ifdef vax
  249.     //
  250.     // What we *really* want to do is push the interrupted pc on the
  251.     // stack and fake a jsb to preempt_reentry, but Ultrix won't
  252.     // let us change the stack pointer (sigh).  Instead we save
  253.     // the interrupted pc in the thread object, set our sigcontext
  254.     // pc to preempt_reentry and fix things up to look like an
  255.     // interrupt occurred once we get there.
  256.     //
  257.     thisthread->setpc(scp->sc_pc);
  258. #endif /* vax */
  259. #ifdef sun
  260.     thisthread->setpc(scp->sc_pc);
  261. #endif /* sun */
  262.  
  263. #ifdef mips
  264.         // This will ensure that once preemption is complete, and we
  265.         // attempt to get back to the stack of the "interrupted"
  266.         // thread, we will in fact begin a test and set operation,
  267.         // thus assuring its "atomicity" (after original by Raj)
  268.  
  269.     
  270.        if (scp->sc_pc >= (int) atomic_test_and_set &&
  271.        scp->sc_pc < (int) atomic_test_and_set_rcs)
  272.              scp->sc_pc = (int)atomic_test_and_set;
  273.          
  274.         // copy the return context into an area immediately above
  275.         // the stack of the interrupted thread. Note that it is not
  276.         // "not exactly the world's most bullet-proof assumption"
  277.         // that such space will exist, but it seems to work fine.
  278.  
  279.         newscp = (struct sigcontext *) (scp->sc_regs[29] - sizeof (struct sigcontext));
  280.         bcopy (scp, newscp, sizeof (struct sigcontext));
  281.  
  282.         // set up the return context so that we can call
  283.         // preempt_preempt(), and then pop back to the original
  284.         // preempted thread (when its get run again).
  285.  
  286.         scp->sc_regs[4] = (int)newscp;
  287.         scp->sc_regs[5] = (int)newscp; // bogus, but I'm not sure if a
  288.                                     // 2nd argument affects the
  289.                        // reset of the stack pointer
  290.  
  291.         // update the stack pointer                       
  292.  
  293.         scp->sc_regs[29] = (int)newscp - (2 * sizeof (int)); 
  294.                                                          
  295.         // this is the function that will call preempt_preempt(),
  296.         // and then sigreturn() back to the context stored at newscp.
  297.  
  298.         scp->sc_pc = (int)mips_preempt_reentry;
  299. #else
  300.        scp->sc_pc = (int)preempt_reentry;
  301. #endif /* mips */
  302.  
  303.     //
  304.     // block the incoming signal until we are through preempting
  305.     //
  306.     osigmask = scp->sc_mask;
  307.     scp->sc_mask |= sigmask(sig);        
  308.     
  309.     numnotifies++;
  310.     
  311. #ifdef mips
  312.     return;
  313. #else
  314.         return 0;
  315. #endif
  316. }                
  317.  
  318. //
  319. //
  320. // VTALRM handler for process designated to control preemption.
  321. // We are a friend of the scheduler
  322. //
  323.  
  324. sigvec_handler_t
  325. sigpreempt_alrm( int sig, int code, struct sigcontext *scp )
  326. {
  327.     Process    *p;
  328.     Thread *t;
  329.     int numtopreempt;
  330.     int i;
  331.  
  332.     
  333.     numtopreempt = sched->readyqlen();
  334.     
  335.     // for fairness, we should cycle through modulo the number
  336.     // of processors, not always start at the beginning.
  337.     //
  338.     numalarms++;
  339.         
  340.     double q = ((double)sched->quantum()) / 1000.0;
  341.     for (i = 0; numtopreempt && i < sched->sc_p_activeschedulers; i++) {
  342.         p = sched->sc_p_procs[i];
  343.  
  344.         // slight race condition... we could get the running
  345.         // thread just as it is returning to the scheduler.  We
  346.         // only can consider a NOTIFY interrupt as being
  347.         // a hint that we should really preempt.
  348.         
  349.         t = p->runningthread();
  350.         if (t && t->canpreempt())        {
  351.                // DESIGNATED PROC MUST BE PREEMPTED
  352.             // Save ourselves from having to handle yet another
  353.             // signal.
  354.                if (p == thisproc)    {
  355.                 (void)sigpreempt_notify(sig, code, scp);
  356.             } else    {
  357.                 // just signal the other proc
  358.                 kill(p->pid(), SIGPREEMPT_NOTIFY);
  359.             }
  360.             numtopreempt--;
  361.         } 
  362.     }
  363. #ifdef mips
  364.     return;
  365. #else
  366.     return 0;
  367. #endif
  368. }
  369.  
  370.     
  371.     
  372. //
  373. //
  374. // Signal handler reentry point.  From the standpoint of the system,
  375. // it looks as though the currently executing thread falls into this
  376. // routine.
  377. //
  378. // preempt_reentry_() is a never-called label.
  379. //
  380.  
  381. #ifndef mips
  382. #ifdef vax
  383. int _hack;
  384. #endif /* vax */
  385. #ifdef sun 
  386.     int _hack;
  387. #endif /* sun */
  388.  
  389. void
  390. preempt_reentry_()        
  391. {
  392.     void preempt_preempt();
  393. asm("    .globl    _preempt_reentry");
  394. asm("_preempt_reentry:");
  395.  
  396.     // must be sure to run off own frame in preempt_preempt.  At this
  397.     // point, we are on the frame of the interrupted routine.
  398.  
  399. #ifdef vax
  400.     //
  401.     // The following hack is compiler-dependent and dangerous.
  402.     // We are assuming that getpc uses no registers besides r0.
  403.     // Also, this won't work on a multiprocessor because we are
  404.     // using a global (_hack) as a temporary for the interrupted
  405.     // pc.  This is a "temporary" uniprocessor vax hack.  The code
  406.     // should be changed to use a faked jsb/rsb when the Ultrix
  407.     // bug alluded to in sigpreempt_notify is fixed.
  408.     //
  409.     // Here's the plan:
  410.     // 1. Push the psl and r0 on the stack.
  411.     // 2. Get the interrupted pc in _hack, push it on the stack.
  412.     // 3. Call preempt_preempt().
  413.     // 4. Sleazily restore r0.
  414.     // 5. Pop the pushed pc, put it where r0 was on the stack.
  415.     // 6. Restore the pc and psl with an REI.
  416.     //
  417.     asm("movpsl -(sp)");
  418.     asm("pushl r0");
  419.     _hack = thisthread->getpc();
  420.     asm("pushl __hack");
  421. #endif /* vax */
  422. #ifdef sun
  423.     //
  424.     // We are doing a similar thing on the sun, only since I don't
  425.     // know what to do with the status register (can't find the
  426.     // *!$! mnemonic), I just ignore it.
  427.     //
  428.     asm("movl a0, sp@-");
  429.     _hack = thisthread->getpc();
  430.     asm("movl __hack, sp@-");
  431. #endif /* sun */
  432.  
  433.     preempt_preempt();
  434.  
  435.     // simulate a rti
  436. #ifdef ns32000
  437.          asm("lprw      upsr,2(sp)");
  438.          asm("adjspb    -4");
  439.          asm("ret       0");
  440. #endif
  441. #ifdef    i386
  442.     asm("popfl");
  443.     asm("ret");
  444. #endif
  445. #ifdef sun
  446.     //
  447.     // Stack should look like:  sp-> PC
  448.     //                 a0
  449.     //
  450.     // We are doing this:            PC
  451.     //                sp-> PC
  452.     //
  453.     asm("movl sp@(4), a0");
  454.     asm("movl sp@, sp@(4)");
  455.     asm("addql #4, sp   ");
  456.     asm("rts");
  457. #endif /* sun */
  458.  
  459. //
  460. // Vax stack looks like:     sp-> PC
  461. //                  r0
  462. //                  PSL
  463. //
  464. // What we are doing is this:
  465. //                      PC
  466. //                 sp-> PC
  467. //                  PSL
  468. // 
  469. #ifdef vax
  470.     asm("movl 4(sp), r0");
  471.     asm("movl (sp), 4(sp)");
  472.     asm("addl2 $4, sp");
  473.     asm("rei");
  474. #endif
  475.  
  476. }
  477.  
  478. #else /* mips */
  479.  
  480. // This is a wierd hack. We call the actual preemption
  481. // routine having already returned from the signal handler.
  482. // However, we saved the context that existed when we were
  483. // were interrupted, in a sigcontext block stored at scp.
  484. // When we're done preempting (presumably back in this
  485. // thread again), we call the well-hidden kernel entry
  486. // point sigreturn() (this is NOT documented anywhere) to
  487. // restore that context for us, so that we appear to just
  488. // carry on where we left off when the signal came in.
  489.  
  490. // Note that carrying on may mean restarting a test and set
  491. // operation, since the MIPS has no atomic instructions.
  492.  
  493. void
  494. mips_preempt_reentry (struct sigcontext *scp, int foo)
  495.  
  496. {
  497.     void preempt_preempt();
  498.  
  499.     foo=foo; // satisfy cfront
  500.     preempt_preempt ();
  501.     sigreturn (scp);
  502. }
  503. #endif
  504.  
  505. //
  506. // preempt_preempt()
  507. //
  508. // we are running as though we were called synchronously
  509. // by the currently executing thread.  Place the current
  510. // thread on the ready queue and swtch back to the scheduler 
  511. // thread.  For a tiny instant, the thread we place on the
  512. // readyqueue will remain locked until the scheduler thread
  513. // unlocks it.  This will only be a problem if some other
  514. // processor grabs it off the ready queue too soon.  But, 
  515. // the fact that we are preempting means that the readyqueue
  516. // is long, so it is not likely that this will happen.  Even if
  517. // it does, it only means that the scheduler thread which pulls
  518. // it off will briefly spin.
  519. //
  520. void
  521. preempt_preempt()
  522. {
  523.     int checkpreempt_request();
  524.     
  525.     //
  526.     // save scratch registers
  527.     //
  528. #ifdef mc68020
  529.     {
  530.      asm("movl a0, sp@- ");
  531.      asm("movl a1, sp@- ");
  532.      asm("movl a2, sp@- ");
  533.     }
  534. #endif /* mc68020 */
  535. #ifdef ns32000
  536.     {
  537.      asm("movd    r0, tos");
  538.      asm("movd    r1, tos");
  539.      asm("movd    r2, tos");
  540.     }
  541. #endif
  542. #ifdef    i386
  543.     {
  544.      asm("pushl    %eax");
  545.      asm("pushl    %ecx");
  546.      asm("pushl    %edx");
  547.     }
  548. #endif
  549. #ifdef vax
  550.     asm("movl r0, -(sp)");
  551.     asm("movl r1, -(sp)");
  552.     asm("movl r2, -(sp)");
  553. #endif
  554. #ifdef mips
  555.         // The mips swtch code does the register save for us
  556. #endif /* mips  */
  557.  
  558.     
  559.     //
  560.     // signal callout mechanism belongs here
  561.     //
  562.     if (checkpreempt_request())    {
  563.         sched->resume(thisthread);
  564.         sigpreempt_unblock();
  565.         if (thisthread->inspinlock())
  566.             error("Preempted thread holds a lock!");
  567.         thisthread->swtch();
  568.     } else    {
  569.         numfalsehits++;
  570.         sigpreempt_unblock();
  571.     }
  572.  
  573. #ifdef mc68020
  574.     {
  575.      asm("movl sp@+, a2 ");
  576.      asm("movl sp@+, a1 ");
  577.      asm("movl sp@+, a0 ");
  578.     }
  579. #endif /* mc68020 */
  580. #ifdef ns32000
  581.         {
  582.     // replace scratch registers
  583.      asm("movd    tos, r2");
  584.      asm("movd    tos, r1");
  585.      asm("movd    tos, r0");
  586.      
  587.         }
  588. #endif
  589. #ifdef    i386
  590.     {
  591.      asm("popl    %edx");
  592.      asm("popl    %ecx");
  593.      asm("popl    %eax");
  594.     }
  595. #endif
  596. #ifdef vax
  597.     asm("movl (sp)+, r2");
  598.     asm("movl (sp)+, r1");
  599.     asm("movl (sp)+, r0");
  600. #endif /* vax */
  601. }
  602.  
  603. //
  604. // Verify that the currently executing thread really wants to be
  605. // preempted.
  606. //
  607.  
  608. static int
  609. checkpreempt_request()
  610. {
  611.     if (thisthread->canpreempt())
  612.         return 1;
  613. #ifndef PREEMPT_DEBUG
  614.     else
  615.         return 0;
  616. #else
  617.     //
  618.     // try to get a little more specific about why it can't be
  619.     //
  620.     if ( !thisthread->ispreemptable())    {
  621.         cerr << thisthread->flags() 
  622.              << "Bad Preempt: Thread is not preemptaable\n";
  623.         return 0;
  624.     } else if (thisthread->flags()&TF_SCHEDULER)    {
  625.         cerr  << "Bad Preempt: scheduler is running\n";
  626.         return 0;
  627.     }
  628. #endif
  629. }
  630.  
  631. #endif /* PREEMPT */
  632.  
  633. //
  634. // These functions allow presto kernel code to mark process objects as
  635. // non-interruptible.  This is used by the memory allocation code when
  636. // acquiring and releasing the malloc spinlock, and is also used to
  637. // disable preemption ticks during a context switch.  Note that the
  638. // process is not interruptible even when it is spinning waiting for
  639. // the malloc lock. This is also a problem for Spinlock objects.  XXX
  640. //
  641. // On the sequent a private type is available, and using it for
  642. // interrupts_enabled saves a few instructions on context switches.
  643. // On other machines, each process object has a p_interruptible
  644. // field and public member functions for enabling and disabling
  645. // interrupts on that process object.
  646. //
  647.  
  648.  
  649. #ifdef sun
  650. #   ifdef THREAD_HAS_INTERRUPTIBLE_FIELD
  651.     int interrupts_are_enabled() {
  652.         if (preemption_enabled) {
  653.             return thisproc->interruptible();
  654.         } else
  655.             return 0;
  656.     }
  657.  
  658.     int disable_interrupts() {
  659.         if (preemption_enabled) {
  660.             return thisproc->disable_interrupts();
  661.         } else
  662.             return 0;
  663.     }
  664.     
  665.     void enable_interrupts() {
  666.         if (preemption_enabled)
  667.             thisproc->enable_interrupts();
  668.     }
  669. #   else
  670.     int interrupts_are_enabled() { return interrupts_enabled; }
  671.  
  672.     int disable_interrupts() { 
  673.         if (interrupts_enabled)    {
  674.             interrupts_enabled = 0;
  675.             return 1;
  676.         } else
  677.             return 0;
  678.     }
  679.  
  680.     void enable_interrupts() { interrupts_enabled = 1; }
  681. #   endif THREAD_HAS_INTERRUPTIBLE_FIELD
  682. #endif /* sun */
  683.  
  684.  
  685. #ifdef sequent
  686. int
  687. interrupts_are_enabled()
  688. {
  689.     return interrupts_enabled;
  690. }
  691.  
  692. extern int
  693. disable_interrupts()
  694. {
  695.     if (interrupts_enabled)    {
  696.         interrupts_enabled = 0;
  697.         return 1;
  698.  
  699.     } else
  700.         return 0;
  701.  
  702. }
  703.  
  704. extern void
  705. enable_interrupts()
  706. {
  707.     interrupts_enabled = 1;
  708. }
  709.  
  710. #endif /* sequent */
  711.  
  712. #ifdef vax
  713. int
  714. interrupts_are_enabled()
  715. {
  716.     if (preemption_enabled) {
  717.         return thisproc->interruptible();
  718.     } else
  719.         return 0;
  720. }
  721.  
  722. extern int
  723. disable_interrupts()
  724. {
  725.     if (preemption_enabled) {
  726.         return thisproc->disable_interrupts();
  727.     } else
  728.         return 0;
  729. }
  730.  
  731. extern void
  732. enable_interrupts()
  733. {
  734.     if (preemption_enabled)
  735.         thisproc->enable_interrupts();
  736. }
  737.  
  738. #endif /* vax */
  739.  
  740. #ifdef mips
  741. int
  742. interrupts_are_enabled()
  743. {
  744.     if (preemption_enabled) {
  745.         return thisproc->interruptible();
  746.     } else
  747.         return 0;
  748. }
  749.  
  750. extern int
  751. disable_interrupts()
  752. {
  753.     if (preemption_enabled) {
  754.         return thisproc->disable_interrupts();
  755.     } else
  756.         return 0;
  757. }
  758.  
  759. extern void
  760. enable_interrupts()
  761. {
  762.     if (preemption_enabled)
  763.         thisproc->enable_interrupts();
  764. }
  765.  
  766. #endif /* mips */
  767.